package net.jalbum.filters;

import java.awt.Graphics;
import java.awt.image.BufferedImage;

import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

import net.jalbum.filterManager.FilterManager;
import net.jalbum.filterManager.FilterProperties.FilterCategory;
import se.datadosen.jalbum.Msg;

public class BlurFilterPlugin extends BasicFilter {

    private static class Pixels {

        private int width;
        private int height;
        private int[] pixels;
        private int[] newPixels;
        private BufferedImage img;

        public Pixels(BufferedImage in, boolean alwaysCopy) {
            this.width = in.getWidth();
            this.height = in.getHeight();
            newPixels = new int[width * height];

            DataBuffer db = in.getRaster().getDataBuffer();
            if (!(db instanceof DataBufferInt) || alwaysCopy) {
                img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                Graphics g = img.getGraphics();
                g.drawImage(in, 0, 0, null);
                g.dispose();
                db = img.getRaster().getDataBuffer();
            } else {
                img = in;
            }

            DataBufferInt dbi = (DataBufferInt) db;
            pixels = dbi.getData();
        }

        public BufferedImage getImage() {
            return img;
        }

        private void blurAlongXAndRotate(int radius) {
            int[] rSum = new int[width + 1];
            int[] gSum = new int[width + 1];
            int[] bSum = new int[width + 1];

            for (int y = 0; y < height; y++) {
                int rAcc = 0;
                int gAcc = 0;
                int bAcc = 0;

                for (int x = 0; x < width;) {
                    int col = pixels[y * width + x++];

                    rSum[x] = rAcc += (col >> 16) & 0xff;
                    gSum[x] = gAcc += (col >> 8) & 0xff;
                    bSum[x] = bAcc += (col) & 0xff;
                }

                for (int x = 0; x < width; x++) {
                    int from = x - radius;
                    int to = x + radius + 1;
                    if (from < 0) {
                        from = 0;
                    }
                    if (to > width) {
                        to = width;
                    }
                    int len = to - from;

                    int r = (rSum[to] - rSum[from]) / len;
                    int g = (gSum[to] - gSum[from]) / len;
                    int b = (bSum[to] - bSum[from]) / len;

                    newPixels[x * height + y] = r << 16 | g << 8 | b;
                }
            }

            int[] tmp = pixels;
            pixels = newPixels;
            newPixels = tmp;

            int itmp = height;
            height = width;
            width = itmp;
        }

        public void boxBlur(float radius) {
            int size = width < height ? width : height;
            int r = (int) (radius * size);

            // This really works!
            blurAlongXAndRotate(r);
            blurAlongXAndRotate(r);
        }
    }
    /**
     *
     */
    private static final long serialVersionUID = -2608721221010747854L;
    protected BlurControl blurControl;
    protected int strength = 5;
    protected int passes = 3;

    public BlurFilterPlugin() {
        this.name = Msg.getString(this, "filters.blur.name");
        this.shortName = Msg.getString(this, "filters.blur.shortName");
        this.icon = new ImageIcon(this.getClass().getResource("res/blur.png"));
        this.author = "Markus Persson";
        this.version = "1.0";
        this.category = FilterCategory.OTHER;
        this.description = Msg.getString(this, "filters.blur.description");
        this.prescale = false;
        this.postscale = true;

        this.blurControl = new BlurControl(this);
    }

    @Override
    public BufferedImage renderImage(final BufferedImage bi) {
        Pixels pixels = new Pixels(bi, true);

        float r = strength / 100.0f;
        r = r * r * r;

        for (int i = 0; i < passes; i++) {
            pixels.boxBlur(r * 0.1f);
        }

        return pixels.getImage();
    }

    @Override
    public JPanel getControls() {
        return this.blurControl;
    }

    public void setFilterManager(final FilterManager filterManager) {
        this.filterManager = filterManager;
    }

    public int getStrength() {
        return this.strength;
    }

    public void setStrength(final int radius) {
        this.strength = radius;
    }

    public void adjustStrength(int radius) {
        if (radius > 100) {
            radius = 100;
        } else if (radius < 0) {
            radius = 0;
        }
        this.strength = radius;
        this.renderImage();
    }
}
